﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;

namespace MentalAlchemy.Atomics
{
	#region - Delegate. -
	/// <summary>
	/// [atomic]
	/// 
	/// Delegate for calculation a specific distance measure between two 2D arrays of floats.
	/// </summary>
	/// <param name="agr1">1-st argument.</param>
	/// <param name="agr2">2-nd argument.</param>
	/// <returns>Calculated distance.</returns>
	public delegate float DistanceMeasure2D(float[,] agr1, float[,] agr2);

	/// <summary>
	/// [atomic]
	/// 
	/// Delegate for calculation a specific distance measure between two 1D arrays of floats.
	/// </summary>
	/// <param name="agr1">1-st argument.</param>
	/// <param name="agr2">2-nd argument.</param>
	/// <returns>Calculated distance.</returns>
	public delegate float DistanceMeasure1D(float[] agr1, float[] agr2);
	#endregion

	#region - Enumerations. -
	/// <summary>
	/// [atomic]
	/// 
	/// Enumeration to denote locations of movable points for the bounding rectangle.
	/// </summary>
	public enum BoundingRectPoint { Undefined, UpperLeft, UpperRight, LowerLeft, LowerRight, VerticalResizeUpper, VerticalResizeLower, HorizontalResizeRight, HorizontalResizeLeft, All }
	#endregion

	#region - Interface. -
	/// <summary>
	/// Interface for classification algorithms.
	/// </summary>
	public interface IClassifier : ICloneable
	{
		/// <summary>
		/// Train the probabilistic classifier using given [data] and setting some portion of it as a seed.
		/// </summary>
		void Train(List<TrainingSample> trainData);

		/// <summary>
		/// Performs recognition by the given object description.
		/// </summary>
		/// <param name="obj">Object description.</param>
		/// <returns>Class ID or '-1' if the class is unrecognized.</returns>
		int Recognize(float[,] obj);

		/// <summary>
		/// Perform voting for the given object using training data.
		/// </summary>
		/// <param name="obj">Object description.</param>
		/// <returns>Dictionary of votes per class.</returns>
		Dictionary<int, int> GetClassVotes(float[,] obj);

		/// <summary>
		/// For the given object get probabilities that it belongs to one of known classes.
		/// </summary>
		/// <param name="obj">Object description.</param>
		/// <returns>Dictionary of probabilities per class.</returns>
		Dictionary<int, float> GetClassProbabilities(float[,] obj);
	} 

	/// <summary>
	/// Interface for neural networks.
	/// </summary>
	public interface INeuralNetwork: ICloneable
	{
		/// <summary>
		/// Returns vector of output signals for ANN.
		/// </summary>
		/// <param name="outputs">Resulting output signals array.</param>
		void GetOutputs(out float[] outputs);

		/// <summary>
		/// Calculates network output.
		/// </summary>
		/// <param name="inputs">Input signals.</param>
		void Calculate(float[] inputs);
	}
	#endregion

	#region - Classes. -
	/// <summary>
	/// [atomic]
	/// 
	/// Class to represent point in 3D coordinates.
	/// </summary>
	public class Point3D
	{
		public float X;
		public float Y;
		public float Z;
	}

	/// <summary>
	/// Class for recognizing systems, containing class name, description, and id.
	/// </summary>
	public class ClassInfo
	{
		public static string Name = "";
		public static string Description = "";
		public static int ClassId = -1;
	}

	/// <summary>
	/// [molecule]
	/// 
	/// Inner structure for the quadruple (vector; probability; counts; weight)
	/// </summary>
	[Serializable]
	public struct VectorProb
	{
		public float[] vector;
		public Dictionary<int, float> probs;
		public Dictionary<int, int> counts;
		public float weight;

		public bool IsEmpty()
		{
			return vector == null;
		}

		/// <summary>
		/// Initializes current [VectorProb] object with the given vector.
		/// </summary>
		/// <param name="v">Init vector.</param>
		public void Init(float[] v)
		{
			vector = v;
			probs = new Dictionary<int, float>();
			counts = new Dictionary<int, int>();
			weight = 1;
		}
	} 

	/// <summary>
	/// [molecule]
	/// 
	/// Class to represent random numbers generator without necessity to reinitialize it.
	/// </summary>
	public class ContextRandom
	{
		public static Random rand = new Random();
		public static double NextDouble() {return rand.NextDouble(); }
		public static int Next() {return rand.Next(); }
		public static int Next(int max) { return rand.Next(max); }
		public static int Next(int min, int max) { return rand.Next(min, max); }
		public static bool NextBoolean() { return rand.Next(-1000, 1000) >= 0; }
	}

	/// <summary>
	/// Class, representing training sample for classification and recognition problems.
	/// </summary>
	[Serializable]
	public class TrainingSample
	{
		/// <summary>
		/// Sample's name.
		/// </summary>
		public string Name;

		/// <summary>
		/// Input vector.
		/// </summary>
		public float[,] Data { get; set; }

		/// <summary>
		/// Desired response.
		/// </summary>
		public float[,] Response { get; set; }

		/// <summary>
		/// ID of the class.
		/// </summary>
		public int ClassID { get; set; }
	}

	/// <summary>
	/// [molecule]
	/// 
	/// Class to store fitness value and some optional additional information.
	/// </summary>
	[Serializable]
	public class Fitness
	{
		#region - Public properties. -
		public float Value { get; set; }
		public List<float> Extra { get; set; }
		#endregion

		#region - Construction. -
		public Fitness()
		{
			Extra = new List<float>();
		}
		public Fitness(Fitness argFitness)
		{
			Assign(argFitness);
		}
		public Fitness(float val)
		{
			Value = val;
			Extra = new List<float>();
		}
		#endregion

		#region - Public methods. -
		public Fitness Clone() { return new Fitness(this); }

		#region - Assignment. -
		public Fitness Assign(Fitness argFitness)
		{
			Value = argFitness.Value;
			Extra = new List<float>(argFitness.Extra);
			return this;
		}

		public Fitness Assign(float argD)
		{
			Value = argD;
			Extra.Clear();
			return this;
		}
		#endregion

		#region - Arithmetics. -
		public static Fitness operator -(Fitness arg1, Fitness arg2)
		{
			var res = new Fitness();
			res.Value = arg1.Value - arg2.Value;

			var size = arg1.Extra.Count;
			res.Extra = new List<float>(size);
			for (int i = 0; i < size; i++)
			{
				res.Extra.Add(arg1.Extra[i] - arg2.Extra[i]);
			}

			//res.Extra = new List<float>(VectorMath.Sub(arg1.Extra.ToArray(), arg2.Extra.ToArray()));
			return res;
		}
		#endregion

		#region - Comparison. -
		public bool Equals(Fitness fit)
		{
			if (Extra.Count != fit.Extra.Count) { return false; }
			var size = Extra.Count;
			for (int i = 0; i < size; i++)
			{
				if (Extra[i] != fit.Extra[i]) return false;
			}
			return Value == fit.Value;
		}
		#endregion

		#region - Utility methods. -
		public void Clear()
		{
			Value = 0f;
			Extra.Clear();
		}

		public void Print(TextWriter writer)
		{
			var res = Value.ToString();
			if (Extra.Count > 0)
			{
				res += " ";
				foreach (var extra in Extra)
				{
					res += "[" + extra + "]";
				}
			}
			writer.Write(res);
		}

		public override string ToString()
		{
			var res = Value.ToString();
			if (Extra.Count > 0)
			{
				res += " ";
				foreach (var extra in Extra)
				{
					res += "[" + extra + "]";
				}
			}
			return res;
		}
		#endregion
		#endregion
	}

	/// <summary>
	/// Class to represent abstract fitness function to be used by evolutionary
	/// and neuroevolutionary algorithms.
	/// </summary>
	public abstract class FitnessFunction
	{
		public abstract Fitness Compute(float[] v);
	}

	/// <summary>
	/// Class to represent node on 2d plane.
	/// The node can have weighted connections with weights defined by [W] property.
	/// Also this class allows deactivation of the node by toggling its [IsActive] property.
	/// </summary>
	[Serializable]
	public class Node2D
	{
		/// <summary>
		/// Weights vector.
		/// </summary>
		public List<float> W { get; set; }

		/// <summary>
		/// Node's coordinates on 2d plane.
		/// </summary>
		public PointF Coord { get; set; }

		/// <summary>
		/// Indicates whether node is active or not.
		/// </summary>
		public bool IsActive { get; set; }
	}

	/// <summary>
	/// [atomic]
	/// 
	/// Class to represent basic weighted edge.
	/// </summary>
	[Serializable]
	public class Edge
	{
		public int BeginIdx { get; set; }
		public int EndIdx { get; set; }
		public float Weight { get; set; }
		public bool Enabled { get; set; }

		#region - Construction. -
		public Edge() { }
		public Edge(int begin, int end, float w)
		{
			BeginIdx = begin;
			EndIdx = end;
			Weight = w;
			Enabled = true;
		}
		public Edge(Edge edge)
		{
			BeginIdx = edge.BeginIdx;
			EndIdx = edge.EndIdx;
			Weight = edge.Weight;
			Enabled = edge.Enabled;
		}
		#endregion
		
		public Edge Clone() {return new Edge(this); }
		
		/// <summary>
		/// Returns [true] if two edges have the same direction regardless to their weights.
		/// </summary>
		/// <param name="edge">Edge to compare.</param>
		/// <returns>[true] if two edges have the same direction regardless to their weights and [false] otherwise.</returns>
		public bool EqualDirection (Edge edge)
		{
			return ((BeginIdx == edge.BeginIdx) && (EndIdx == edge.EndIdx));
		}
	}

	/// <summary>
	/// Class to represent parameters for 2D Gaussian Envelope which is used for signal/image processing.
	/// 
	/// The parameters usage in Gaussian calculation (from the 'Tutorial on Gabor Filters' by Javier R. Movellan):
	/// 
	///		G(x,y) = Magnitude \exp{ -\pi (ScaleX^2 (x-Center.X)_r^2 + ScaleY^2 (y-Center.Y)_r^2)}
	/// 
	/// where
	/// 
	///		(x-Center.X)_r = (x-Center.X) \cos{Angle} + (y-Center.Y) \sin{Angle}
	///		(y-Center.Y)_r = -(x-Center.X) \sin{Angle} + (y-Center.Y) \cos{Angle}
	/// 
	/// </summary>
	[Serializable]
	public class GaussianEnvelopeParameters
	{
		public PointF Center = PointF.Empty;	// Gaussian center.
		public float Angle = 0.0f;		// Rotation value for Gaussian.
		public float ScaleXSqr = 1.0f;	// Squared(!) scale along X axis.
		public float ScaleYSqr = 1.0f;	// Squared(!) scale along Y axis.
		public float Magnitude = 1.0f;	// Magnitude value.
	}

	/// <summary>
	/// [atomic]
	/// 
	/// Class to represent directed graphs.
	/// </summary>
	[Serializable]
	public class Graph
	{
		/// <summary>
		/// Graph edges.
		/// </summary>
		public List<Edge> Edges { get; set;}

		/// <summary>
		/// Adjacency matrix of the graph.
		/// Needs checking for null value!
		/// </summary>
		public float[,] AdjacencyMatrix { get; set; }
	}

	/// <summary>
	/// Structure to represent basic statistic measures.
	/// </summary>
	[Serializable]
	public class Stats
	{
		public float Min;
		public float Max;
		public float Mean;
		public float Median;
		public float Variance;
		public float Total;

		public List<float> Data = new List<float>();	// additional data.

		public string Header = "Min\tMax\tMean\tVariance\tMedian\tTotal";

		/// <summary>
		/// Returns header, which enumerates stats names in the order, correspondent to stats output in the [Write] method.
		/// </summary>
		/// <returns>Header string.</returns>
		public string GetStatsHeader () {return Header;}

		/// <summary>
		/// Adds custom data value to the stats.
		/// </summary>
		/// <param name="value">Value to append.</param>
		/// <param name="name">Data name.</param>
		public void AppendData (float value, string name)
		{
			Data.Add(value);
			Header += "\t" + name;
		}

		/// <summary>
		/// Converts given [Stats] variable into string representation with values separated by tabs.
		/// </summary>
		/// <returns>String representation of the stats.</returns>
		public string GetStatsString()
		{
			string res = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", Min, Max, Mean, Variance, Median, Total);
			for (int i = 0; i < Data.Count; i++)
			{
				res += "\t" + Data[i];
			}
			return res;
		}
	}

	/// <summary>
	/// Class to represent 2D line.
	/// </summary>
	[Serializable]
	public class Line
	{
		public PointF Begin { get; set; }
		public PointF End { get; set; }
	}
	#endregion
}
